﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MetroGA.Core;
using MetroGA.Core.Hits;
using Newtonsoft.Json;

namespace MetroGA.Wp8
{
	internal class Analytics : AnalyticsCore
	{
		private const string StoreFilename = "metroga.store";

		private readonly Mutex _fileAccessMutex;

		public Analytics(string trackerId, string applicationId, string applicationName, string applicationVersion)
			: base(new InformationProvider(), new SessionContainer(), trackerId, applicationId, applicationName, applicationVersion)
		{
			_fileAccessMutex = new Mutex(false, ApplicationId + ".MetroGA");
			Initialize();
		}

		protected async override Task<bool> SendHit(Hit hit)
		{
			try
			{
				var webRequest = (HttpWebRequest)WebRequest.Create(AnalyticsBaseUri);
				webRequest.UserAgent = String.Format("{0}/{1} ({2}; U; {3}; {4})", hit.ApplicationName, hit.ApplicationVersion, InformationProvider.PlatformName, InformationProvider.PlatformVersion, InformationProvider.Culture);
				webRequest.Headers[HttpRequestHeader.CacheControl] = "no-store, no-transform, no-cache";
				webRequest.Headers[HttpRequestHeader.Pragma] = "no-cache";
				webRequest.Method = "POST";
				using (var requestStream = await Task.Factory.FromAsync<Stream>(webRequest.BeginGetRequestStream, webRequest.EndGetRequestStream, null))
				{
					var content = Encoding.UTF8.GetBytes(String.Join("&", hit.ToKeyValuePairs().Select(kvp => kvp.Key + "=" + Uri.EscapeDataString(kvp.Value))));
					requestStream.Write(content, 0, content.Length);
				}

				var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(webRequest.BeginGetResponse, webRequest.EndGetResponse, null);
				return (int)response.StatusCode < 400;
			}
			catch (Exception)
			{
				return false;
			}
		}

		protected override async Task<IEnumerable<Hit>> LoadPersistedHitsAsync()
		{
			_fileAccessMutex.WaitOne();
			try
			{
				if (IsolatedStorageFile.GetUserStoreForApplication().FileExists(StoreFilename))
				{
					using (var stream = IsolatedStorageFile.GetUserStoreForApplication().OpenFile(StoreFilename, FileMode.Open))
					using (var reader = new StreamReader(stream))
					{
						var content = reader.ReadToEnd();

						if (!String.IsNullOrWhiteSpace(content))
							return JsonConvert.DeserializeObject<IEnumerable<Hit>>(content,
							                                                       new JsonSerializerSettings
								                                                       {
									                                                       TypeNameHandling = TypeNameHandling.Objects,
									                                                       Binder = new HitTypeBinder(),
									                                                       ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
								                                                       })
							                  .ToList();
					}
				}
			}
			catch (Exception)
			{ }
			finally
			{
				_fileAccessMutex.ReleaseMutex();
			}

			return Enumerable.Empty<Hit>().ToList();
		}

		protected override void PersistHits(IEnumerable<Hit> hits)
		{
			_fileAccessMutex.WaitOne();
			try
			{
				var content = JsonConvert.SerializeObject(hits,
				                                          Formatting.None,
				                                          new JsonSerializerSettings
					                                          {
						                                          Binder = new HitTypeBinder(),
						                                          TypeNameHandling = TypeNameHandling.Objects,
					                                          });

				using (var storeFile = IsolatedStorageFile.GetUserStoreForApplication().OpenFile(StoreFilename, FileMode.Create))
				{
					var bytes = Encoding.UTF8.GetBytes(content);
					storeFile.Write(bytes, 0, bytes.Length);
					storeFile.Flush(true);
				}
			}
			catch (Exception)
			{ }
			finally
			{
				_fileAccessMutex.ReleaseMutex();
			}
		}
	}
}